﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Undercover.Model
{
    public class Game
    {
        public enum ActorRole { Majority, Minority, Agent };
        public enum GameStage { Setup, DiscussAndVote, Guess, AgentWin };

        public GameStage Stage {get; private set;}
        public int Round { get; private set; }

        public WordPair Words { get; private set; }
        public int NumberOfMajorities { get; private set; }
        public int NumberOfMinorities { get; private set; }
        public int NumberOfAgents { get; private set; }

        public int NumberOfPlayers { get; private set; }
        public List<ActorRole> PlayerRoles { get; private set; }
        public List<int> TheDeads { get; private set; }
        public List<int> TheAgents { get; private set; }

        public Game()
        {
            Stage = GameStage.Setup;
            Round = 0;
        }

        public void Setup(int nMajorities, int nMinorities, int nAgents, WordPair words)
        {
            if (Stage != GameStage.Setup)
            {
                throw new InvalidOperationException();
            }

            if (nMajorities <= 0 || nMinorities <= 0 || nAgents <= 0 || nMajorities < nMinorities || nAgents < nMinorities)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (words == null)
            {
                throw new ArgumentNullException();
            }

            NumberOfMajorities = nMajorities;
            NumberOfMinorities = nMinorities;
            NumberOfAgents = nAgents;
            Words = words;
            NumberOfPlayers = NumberOfMajorities+NumberOfMinorities+NumberOfAgents;

            TheAgents = new List<int>();
            // Shuffle the roles
            {
                int[] p = new int[NumberOfPlayers];
                p[0] = 0;

                for (int i = 1; i < p.Length; ++i)
                {
                    p[i] = i;
                    int j = random.Next(i+1);
                    if (i!=j)
                    {
                        int temp = p[j];
                        p[j] = p[i];
                        p[i] = temp;
                    }                    
                }

                PlayerRoles = new  List<ActorRole>();
                for (int i = 0; i < p.Length; ++i)
                {
                    if (p[i] < nMajorities)
                    {
                        PlayerRoles.Add(ActorRole.Majority);
                    }
                    else if (p[i] - nMajorities < nMinorities)
                    {
                        PlayerRoles.Add(ActorRole.Minority);
                    }
                    else
                    {
                        TheAgents.Add(PlayerRoles.Count);
                        PlayerRoles.Add(ActorRole.Agent);
                    }
                }
            }

            TheDeads = new List<int>();
            Round = 1;
            Stage = GameStage.DiscussAndVote;
        }

        public void Vote(int ID)
        {
            if (Stage != GameStage.DiscussAndVote)
            {
                throw new InvalidOperationException();
            }

            if (ID < 0 || ID >= NumberOfPlayers)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (TheDeads.IndexOf(ID) > -1)
            {
                throw new ArgumentOutOfRangeException("This player has already been dead.");
            }

            TheDeads.Add(ID);

            int nDeadMajorities = TheDeads.Count((int id) => PlayerRoles[id] == ActorRole.Majority);
            int nDeadMinorities = TheDeads.Count((int id) => PlayerRoles[id] == ActorRole.Minority);
            int nDeadAgents = TheDeads.Count((int id) => PlayerRoles[id] == ActorRole.Agent);

            if (nDeadMajorities == NumberOfMajorities)
            {
                Stage = GameStage.AgentWin;
            }
            else if (nDeadMinorities == NumberOfMinorities || nDeadAgents == NumberOfAgents)
            {
                Stage = GameStage.Guess;
            }
            else
            {
                ++Round;
            }
        }

        private static Random random = new Random((int)DateTime.UtcNow.Ticks);
    }
}
